﻿///////////////////////////////////////////////////////////////////////////////
// File:        Server.hpp
// Author:      671643387@qq.com
// Description:
///////////////////////////////////////////////////////////////////////////////

#ifndef NIO_NET_SERVER_HPP_
#define NIO_NET_SERVER_HPP_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cassert>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/unordered_map.hpp>
#include <vector>
#include <nio/net/EventLoop.hpp>
#include <nio/net/ConnectionFactory.hpp>
#include <nio/net/Acceptor.hpp>
#include <nio/net/ServerThread.hpp>

namespace nio
{
    namespace net
    {
        class Server : public boost::noncopyable
        {
        public:
            enum State
            {
                READY,
                RUNNING,
                EXITING,
                EXIT
            };

            struct Config
            {
                int threads;
                base::time_t threadFrameTime;
                base::time_t serverFrameTime;
                base::time_t connectionHeartbeatTime;
                bool useSockBuffSet;
                unsigned int sndBuffSize;
                unsigned int rcvBuffSize;
                unsigned int connectionInputStreamSize;
                unsigned int connectionOutputStreamSize;
                bool connectionUserIO;
#if defined(__LINUX__)
                bool epollET;
#endif
            };

            struct Statics
            {
                State state;
                int connIdGen;
                int connLoad;
                int connTotal;
                base::time_t frameUseTime;
                unsigned long long bytsSend;
                unsigned long long bytsRecv;
                std::vector<ServerThread::Statics> threadsStatics;
            };
        public:
            Server(void);
            ~Server(void);

            bool open(std::string addr, int port, int backlog);
            void close(void);
            void run(void);
            State getState(void) const { return state_; }
            EventLoop& getLoop(void) { return eventLoop_; }

            void config_Threads(int threads)
            {
                assert(state_ == READY);
                cnfThreads_ = threads;
            }

            void config_ThreadFrameTime(base::time_t time)
            {
                assert(state_ == READY);
                cnfThreadFrameTime_ = time;
            }

            void config_ServerFrameTime(base::time_t time)
            {
                assert(state_ == READY);
                cnfServerFrameTime_ = time;
            }

            void config_ConnectionHeartbeatTime(base::time_t time)
            {
                assert(state_ == READY);
                cnfConnectionHeartbeatTime_ = time;
            }

            void config_UseSockBuffSizeSet(bool on)
            {
                assert(state_ == READY);
                cnfUseSockBuffSet_ = on;
            }

            void config_ConnectionSendBuffSize(unsigned int size)
            {
                assert(state_ == READY);
                cnfSndBuffSize_ = size;
            }

            void config_ConnectionRecvBuffSize(unsigned int size)
            {
                assert(state_ == READY);
                cnfRcvBuffSize_ = size;
            }

            void config_ConnectionInputStreamSize(unsigned int size)
            {
                assert(state_ == READY);
                cnfConnectionInputStreamSize_ = size;
            }

            void config_ConnectionOutputStreamSize(unsigned int size)
            {
                assert(state_ == READY);
                cnfConnectionOutputStreamSize_ = size;
            }

            void config_ConnectionUserIO(bool on)
            {
                assert(state_ == READY);
                cnfConnectionUserIO_ = on;
            }

#if defined(__LINUX__)
            void config_EpollET(bool on)
            {
                assert(state_ == READY);
                cnfEpollET_ = on;
            }
#endif

            void setConnectionFactory(ConnectionFactory* factory)
            {
                assert(state_ == READY);
                connFactory_.reset(factory);
            }

            void setConnectionReabledFN(Connection::ConnectionReadableFN fn)
            {
                assert(state_ == READY);
                connReadableFN_ = fn;
            }

            void setConnectionWritableFN(Connection::ConnectionWritableFN fn)
            {
                assert(state_ == READY);
                connWritableFN_ = fn;
            }

            void setConnectionErrorFN(Connection::PassConnectionFN fn)
            {
                assert(state_ == READY);
                connErrorFN_ = fn;
            }

            void setConnectionHeartbeatFN(Connection::PassConnectionFN fn)
            {
                assert(state_ == READY);
                connHeatbeatFN_ = fn;
            }

            void setConnectionCloseFN(Connection::PassConnectionFN fn)
            {
                assert(state_ == READY);
                connCloseFN_ = fn;
            }

            void setConnectionEstablishedFN(boost::function<void(ConnectionPtr&)> fn)
            {
                assert(state_ == READY);
                connectionEstablishedFN_ = fn;
            }

            void getConfig(struct Config& config)
            {
                config.threads = cnfThreads_;
                config.threadFrameTime = cnfThreadFrameTime_;
                config.serverFrameTime = cnfServerFrameTime_;
                config.connectionHeartbeatTime = cnfConnectionHeartbeatTime_;
                config.useSockBuffSet = cnfUseSockBuffSet_;
                config.sndBuffSize = cnfSndBuffSize_;
                config.rcvBuffSize = cnfRcvBuffSize_;
                config.connectionInputStreamSize = cnfConnectionInputStreamSize_;
                config.connectionOutputStreamSize = cnfConnectionOutputStreamSize_;
                config.connectionUserIO = cnfConnectionUserIO_;
#if defined(__LINUX__)
                config.epollET = cnfEpollET_;
#endif
            }

            void getStatics(struct Statics& statics)
            {
                statics.state = state_;
                statics.connIdGen = connIdGen_;
                statics.connLoad = connLoad_;
                statics.connTotal = connTotal_;
                statics.frameUseTime = eventLoop_.getFrameUseTime();
                statics.bytsSend = bytsSend_;
                statics.bytsRecv = bytsRecv_;

                for (int x = 0; x < cnfThreads_; ++x)
                {
                    ServerThread::Statics tStatics;
                    threads_[x]->getStatics(tStatics);
                    statics.threadsStatics.push_back(tStatics);
                }
            }

        private:
            bool initThreads(void);
            bool startThreads(void);
            void shutdownThreads(void);
            void setState(State state) { state_ = state; }
            void doConnectionAccept(sock_t sock);
            void connectionAdded(ConnectionPtr& conn);
            void doConnectionAdded(ConnectionPtr& conn);
            void shutdownConnection(Connection* conn);
            void doShutdownConnection(Connection* conn);
            void removeConnection(Connection* conn);
            void doRemoveConnection(Connection* conn);
            void shutdownAcceptor(void);
            void doShutdownAcceptor(void);
            void doShutdown(void);

            void inc_ConnTotal(void)
            {
#if defined(__WINDOWS__)
                ::InterlockedIncrement((LONG *)&connTotal_);
#elif defined(__LINUX__)
                ::__sync_add_and_fetch(&connTotal_, 1);
#endif
            }

            void dec_ConnTotal(void)
            {
#if defined(__WINDOWS__)
                ::InterlockedDecrement((LONG*)&connTotal_);
#elif defined(__LINUX__)
                ::__sync_sub_and_fetch(&connTotal_, 1);
#endif
            }

            void byts_Send(unsigned int length)
            {
#if defined(__WINDOWS__)
                ::InterlockedAdd64((long long *)&bytsSend_, (long long)length);
#elif defined(__LINUX__)
                ::__sync_add_and_fetch(&bytsSend_, length);
#endif
            }

            void byts_Recv(unsigned int length)
            {
#if defined(__WINDOWS__)
                ::InterlockedAdd64((long long *)&bytsRecv_, (long long)length);
#elif defined(__LINUX__)
                ::__sync_add_and_fetch(&bytsRecv_, length);
#endif
            }

        private:
            typedef std::vector<ServerThreadPtr>                ServerThreadArray;
            typedef boost::unordered_map<int, ConnectionPtr>    ConnectionMap;

        private:
            State                                   state_;
            int                                     connIdGen_;
            int                                     connLoad_;
            int                                     connTotal_;
            unsigned long long                      bytsSend_;
            unsigned long long                      bytsRecv_;

            int                                     cnfThreads_;
            base::time_t                            cnfThreadFrameTime_;
            base::time_t                            cnfServerFrameTime_;
            base::time_t                            cnfConnectionHeartbeatTime_;
            bool                                    cnfUseSockBuffSet_;
            unsigned int                            cnfSndBuffSize_;
            unsigned int                            cnfRcvBuffSize_;
            unsigned int                            cnfConnectionInputStreamSize_;
            unsigned int                            cnfConnectionOutputStreamSize_;
            bool                                    cnfConnectionUserIO_;
#if defined(__LINUX__)
            bool                                    cnfEpollET_;
#endif

            ConnectionFactoryPtr                    connFactory_;

            Connection::ConnectionReadableFN        connReadableFN_;
            Connection::ConnectionWritableFN        connWritableFN_;
            Connection::PassConnectionFN            connErrorFN_;
            Connection::PassConnectionFN            connHeatbeatFN_;
            Connection::PassConnectionFN            connCloseFN_;

            boost::function<void(ConnectionPtr&)>   connectionEstablishedFN_;

            EventLoop                               eventLoop_;
            Acceptor                                acceptor_;
            ServerThreadArray                       threads_;
            ConnectionMap                           connections_;
        };
    }
}

#endif //NIO_NET_SERVER_HPP_
